(* printVals -- print a list of values *)
local val printVal = print o Int.toString
in val rec printVals : int list -> unit =
    fn [] => print "\n"
(*     | [z] => ( printVal z
	      ; print "\n") *)
     | (z::zs) => ( printVal z
		  ; print " "
		  ; printVals zs)
end;

(* prim -- return the (int * int) -> int procedure associated with the given binop *)
val prim : binop -> (int * int) -> int =
    fn Plus  => op +
     | Minus => op -
     | Times => op *
     | Div   => op div;

(* interpStm : given a table, interpret a statment with it,
	       returning the resulting table *)
val rec interpStm : (table * stm) -> table =
 fn (t, (CompoundStm (s, s'))) => interpStm (interpStm (t, s), s')
  | (t, (AssignStm (i, e))) => let val (t, z) = interpExp (t, e)
			       in update (t, i, z)
			       end
  | (t, (PrintStm es)) => let val (t, zs) = interpExps (t, es)
			  in ( printVals zs
			     ; t)
			  end

(* interpExp : given a table, interpret an expression with it,
               returning the resulting table and the expression's value *)
and interpExp : (table * exp) -> (table * int) =
 fn (t, IdExp i) => (t, lookup (t, i))
  | (t, NumExp z) => (t, z)
  | (t, OpExp (e, p, e')) => let val (t, z) = interpExp (t, e)
			     in let val (t, z') = interpExp (t, e')
				in (t, (prim p) (z, z'))
				end
			     end
  | (t, EseqExp (s, e)) => interpExp (interpStm (t, s), e)

(* interpExps : given a table, interpret a list of expressions with it,
                returning the cumulative final table and the expressions' values *)
and interpExps : table * exp list -> (table * int list) =
 fn (t, []) => (t, [])
  | (t, e::es) => let val (t, z) = interpExp (t, e)
		    in let val (t, zs) = interpExps (t, es)
		       in (t, z::zs)
		       end
		    end;

(* interp -- interpret a program *)
val interp : prg -> unit =
 fn s => ignore (interpStm (empty, s));

(* testing *)
use "progs.sml";

print " 1: "; interp(prog1);  print "\n";
print " 2: "; ( interp(prog2)
	      ; raise Fail "prog2"
	      ) handle (NotFound "i") => print "caught";  print "\n";
print " 3: "; interp(prog3);  print "\n";
print " 4: "; interp(prog4);  print "\n";
print " 5: "; ( interp(prog5)
	      ; raise Fail "prog2"
	      ) handle (NotFound "i") => print "caught";  print "\n";
print " 6: "; interp(prog6);  print "\n";
print " 7: "; interp(prog7);  print "\n";
print " 8: "; interp(prog8);  print "\n";
print " 9: "; interp(prog9);  print "\n";
print "10: "; interp(prog10); print "\n";
print "11: "; interp(prog11); print "\n";
print "12: "; interp(prog12); print "\n";
print "13: "; interp(prog13); print "\n";
print "14: "; interp(prog14); print "\n";
print "15: "; interp(prog15); print "\n";
print "16: "; interp(prog16); print "\n";

OS.Process.exit OS.Process.success;
